package com.chase.framerwork.dao;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.internal.CriteriaImpl.OrderEntry;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.transform.ResultTransformer;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.util.Assert;

import com.chase.framerwork.bean.Pager;
import com.chase.framerwork.bean.enums.OrderType;
import com.chase.framerwork.entity.BaseEntity;
import com.chase.framerwork.util.ReflectionUtil;

/**
 * Dao层最父类,所有Dao都必须继承自该父类
 * 
 * @author Chase
 * 
 * @param <E>
 *            Entity
 * @param <ID>
 *            ID
 */
public class BaseDaoImpl<E, ID extends Serializable> implements BaseDao<E, ID>
{

	private static final String ORDER_LIST_PROPERTY_NAME = "orderList";// "排序"属性名称
	private static final String CREATE_DATE_PROPERTY_NAME = "createDate";// "创建日期"属性名称

	private Class<E> entityClass;

	protected SessionFactory sessionFactory;
	protected HibernateTemplate hibernateTemplate;
	protected JdbcTemplate jdbcTemplate;

	@SuppressWarnings("unchecked")
	public BaseDaoImpl()
	{
		Class<?> c = getClass();
		Type type = c.getGenericSuperclass();
		if (type instanceof ParameterizedType)
		{
			Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();
			this.entityClass = (Class<E>) parameterizedType[0];
		}
	}

	@Resource(name = "sessionFactory")
	public void setSessionFactory(SessionFactory sessionFactory)
	{
		this.sessionFactory = sessionFactory;
	}

	@SuppressWarnings("unchecked")
	public E get(ID id)
	{
		Assert.notNull(id, "id is required");
		return (E) getSession().get(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	public E load(ID id)
	{
		Assert.notNull(id, "id is required");
		return (E) getSession().load(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	public List<E> getAllList()
	{
		ClassMetadata classMetadata = sessionFactory.getClassMetadata(entityClass);
		String hql;
		if (ArrayUtils.contains(classMetadata.getPropertyNames(), ORDER_LIST_PROPERTY_NAME))
		{
			hql = "from " + entityClass.getName() + " as entity order by entity." + ORDER_LIST_PROPERTY_NAME + " desc";
		}
		else
		{
			hql = "from " + entityClass.getName();
		}
		return getSession().createQuery(hql).list();
	}

	public Long getTotalCount()
	{
		String hql = "select count(*) from " + entityClass.getName();
		return (Long) getSession().createQuery(hql).uniqueResult();
	}

	@SuppressWarnings("unchecked")
	public ID save(E entity)
	{
		Assert.notNull(entity, "entity is required");
		if (entity instanceof BaseEntity)
		{
			try
			{
				Method method = entity.getClass().getMethod(BaseEntity.ON_SAVE_METHOD_NAME);
				method.invoke(entity);
				return (ID) getSession().save(entity);
			}
			catch (Exception e)
			{
				e.printStackTrace();
				return null;
			}
		}
		else
		{
			return (ID) getSession().save(entity);
		}
	}

	public void update(E entity)
	{
		Assert.notNull(entity, "entity is required");
		if (entity instanceof BaseEntity)
		{
			try
			{
				Method method = entity.getClass().getMethod(BaseEntity.ON_UPDATE_METHOD_NAME);
				method.invoke(entity);
				getSession().update(entity);
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}
		else
		{
			getSession().update(entity);
		}
	}

	public void delete(E entity)
	{
		Assert.notNull(entity, "entity is required");
		getSession().delete(entity);
	}

	@SuppressWarnings("unchecked")
	public void delete(ID id)
	{
		Assert.notNull(id, "id is required");
		E entity = (E) getSession().load(entityClass, id);
		getSession().delete(entity);
	}

	@SuppressWarnings("unchecked")
	public void delete(ID... ids)
	{
		Assert.notEmpty(ids, "ids must not be empty");
		for (ID id : ids)
		{
			E entity = (E) getSession().load(entityClass, id);
			getSession().delete(entity);
		}
	}

	public void flush()
	{
		getSession().flush();
	}

	public void evict(Object object)
	{
		Assert.notNull(object, "object is required");
		getSession().evict(object);
	}

	public void clear()
	{
		getSession().clear();
	}

	public Pager findPager(Pager pager)
	{
		Criteria criteria = getSession().createCriteria(entityClass);
		return findPager(pager, criteria);
	}

	public Pager findPager(Pager pager, Criterion... criterions)
	{
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Criterion criterion : criterions)
		{
			criteria.add(criterion);
		}
		return findPager(pager, criteria);
	}

	public Pager findPager(Pager pager, Order... orders)
	{
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Order order : orders)
		{
			criteria.addOrder(order);
		}
		return findPager(pager, criteria);
	}

	public Pager findPager(Pager pager, Criteria criteria)
	{
		Assert.notNull(pager, "pager is required");
		Assert.notNull(criteria, "criteria is required");

		Integer pageNumber = pager.getPageNumber();
		Integer pageSize = pager.getPageSize();
		String searchBy = pager.getSearchBy();
		String keyword = pager.getKeyword();
		String orderBy = pager.getOrderBy();
		OrderType order = pager.getOrderType();

		if (StringUtils.isNotEmpty(searchBy) && StringUtils.isNotEmpty(keyword))
		{
			if (searchBy.contains("."))
			{
				String alias = StringUtils.substringBefore(searchBy, ".");
				criteria.createAlias(alias, alias);
			}
			criteria.add(Restrictions.like(searchBy, "%" + keyword + "%"));
		}

		pager.setTotalCount(criteriaResultTotalCount(criteria));

		if (StringUtils.isNotEmpty(orderBy) && order != null)
		{
			if (order == OrderType.asc)
			{
				criteria.addOrder(Order.asc(orderBy));
			}
			else
			{
				criteria.addOrder(Order.desc(orderBy));
			}
		}

		ClassMetadata classMetadata = sessionFactory.getClassMetadata(entityClass);
		if (!StringUtils.equals(orderBy, ORDER_LIST_PROPERTY_NAME)
		        && ArrayUtils.contains(classMetadata.getPropertyNames(), ORDER_LIST_PROPERTY_NAME))
		{
			criteria.addOrder(Order.asc(ORDER_LIST_PROPERTY_NAME));
			criteria.addOrder(Order.desc(CREATE_DATE_PROPERTY_NAME));
			if (StringUtils.isEmpty(orderBy) || order == null)
			{
				pager.setOrderBy(ORDER_LIST_PROPERTY_NAME);
				pager.setOrderType(OrderType.asc);
			}
		}
		else if (!StringUtils.equals(orderBy, CREATE_DATE_PROPERTY_NAME)
		        && ArrayUtils.contains(classMetadata.getPropertyNames(), CREATE_DATE_PROPERTY_NAME))
		{
			criteria.addOrder(Order.desc(CREATE_DATE_PROPERTY_NAME));
			if (StringUtils.isEmpty(orderBy) || order == null)
			{
				pager.setOrderBy(CREATE_DATE_PROPERTY_NAME);
				pager.setOrderType(OrderType.desc);
			}
		}

		criteria.setFirstResult((pageNumber - 1) * pageSize);
		criteria.setMaxResults(pageSize);

		pager.setResult(criteria.list());
		return pager;
	}

	// 获取Criteria查询数量
	@SuppressWarnings("unchecked")
	private int criteriaResultTotalCount(Criteria criteria)
	{
		Assert.notNull(criteria, "criteria is required");

		int criteriaResultTotalCount = 0;
		try
		{
			CriteriaImpl criteriaImpl = (CriteriaImpl) criteria;

			Projection projection = criteriaImpl.getProjection();
			ResultTransformer resultTransformer = criteriaImpl.getResultTransformer();
			List<OrderEntry> orderEntries = (List<OrderEntry>) ReflectionUtil.getFieldValue(criteriaImpl,
			        "orderEntries");
			ReflectionUtil.setFieldValue(criteriaImpl, "orderEntries", new ArrayList<OrderEntry>());

			Integer totalCount = ((Long) criteriaImpl.setProjection(Projections.rowCount()).uniqueResult()).intValue();
			if (totalCount != null)
			{
				criteriaResultTotalCount = totalCount;
			}

			criteriaImpl.setProjection(projection);
			if (projection == null)
			{
				criteriaImpl.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
			}
			if (resultTransformer != null)
			{
				criteriaImpl.setResultTransformer(resultTransformer);
			}
			ReflectionUtil.setFieldValue(criteriaImpl, "orderEntries", orderEntries);
		}
		catch (Exception e)
		{

		}
		return criteriaResultTotalCount;
	}

	@SuppressWarnings("unchecked")
	public List<E> findByHql(String hql)
	{
		return getSession().createQuery(hql).list();
	}

	@SuppressWarnings("unchecked")
	public List<E> findByHql(String hql, Object[] params)
	{
		return getSession().createQuery(hql).setParameterList(hql, params).list();
	}

	@SuppressWarnings("unchecked")
	public List<E> findByHql(String hql, Collection<?> params)
	{
		return getSession().createQuery(hql).setParameterList(hql, params).list();
	}

	public Session getSession()
	{
		return sessionFactory.getCurrentSession();
	}

	public HibernateTemplate getHibernateTemplate()
	{
		return hibernateTemplate;
	}

	public void setHibernateTemplate(HibernateTemplate hibernateTemplate)
	{
		this.hibernateTemplate = hibernateTemplate;
	}

	public JdbcTemplate getJdbcTemplate()
	{
		return jdbcTemplate;
	}

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate)
	{
		this.jdbcTemplate = jdbcTemplate;
	}

	public SessionFactory getSessionFactory()
	{
		return sessionFactory;
	}

}